package com.googlecode.pacswitch;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.text.DateFormat;
import java.util.Date;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;

/**
 * @author Andrey Pavlenko
 */
@XmlType(name = "log", namespace=ObjectFactory.NS)
public class Log {
    private Level _level = Level.INFO;
    private PString _file;
    private PrintStream _out = System.out;
    private final DateFormat _dateFormat = DateFormat.getDateTimeInstance(
            DateFormat.SHORT, DateFormat.SHORT);

    void init(final PacSwitch ps) throws IOException {
        if (_file != null) {
            final File f = new File(_file.resolve(ps, null));
            f.getParentFile().mkdirs();
            _out = new PrintStream(new FileOutputStream(f, true), true);
        }
    }

    void release(final PacSwitch ps) {
        if (_out != System.out) {
            _out.close();
        }
    }

    @XmlAttribute
    public PString getFile() {
        return _file;
    }

    public void setFile(final PString file) {
        _file = file;
    }

    @XmlAttribute
    public Level getLevel() {
        return _level;
    }

    public void setLevel(final Level level) {
        _level = level;
    }

    public void debug(final Object msg) {
        if (isLoggable(Level.DEBUG)) {
            print(msg(msg, Level.DEBUG), null);
        }
    }

    public void info(final Object msg) {
        if (isLoggable(Level.INFO)) {
            print(msg(msg, Level.INFO), null);
        }
    }

    public void warn(final Object msg) {
        if (isLoggable(Level.WARN)) {
            print(msg(msg, Level.WARN), null);
        }
    }

    public void warn(final Object msg, final Throwable ex) {
        if (isLoggable(Level.WARN)) {
            print(msg(msg, Level.WARN), ex);
        }
    }

    public void error(final Object msg) {
        if (isLoggable(Level.ERROR)) {
            print(msg(msg, Level.ERROR), null);
        }
    }

    public void error(final Object msg, final Throwable ex) {
        if (isLoggable(Level.ERROR)) {
            print(msg(msg, Level.ERROR), ex);
        }
    }

    public boolean isDebugEnabled() {
        return isLoggable(Level.DEBUG);
    }

    public boolean isInfoEnabled() {
        return isLoggable(Level.INFO);
    }

    public boolean isWarnEnabled() {
        return isLoggable(Level.WARN);
    }

    public boolean isErrorEnabled() {
        return isLoggable(Level.ERROR);
    }

    public boolean isLoggable(final Level level) {
        return level.ordinal() >= _level.ordinal();
    }

    private void print(final String msg, final Throwable ex) {
        synchronized (_out) {
            _out.println(msg);

            if (ex != null) {
                ex.printStackTrace(_out);
            }
        }
    }

    private String msg(final Object msg, final Level level) {
        final Thread t = Thread.currentThread();
        final StackTraceElement ste = t.getStackTrace()[3];
        String className = ste.getClassName();
        final int dot = className.lastIndexOf('.');

        if (dot != -1) {
            className = className.substring(dot + 1);
        }

        return "[" + _dateFormat.format(new Date()) + "][" + level + "] " + msg
                + " #[" + className + "." + ste.getMethodName() + "("
                + ste.getFileName() + ":" + ste.getLineNumber() + ")]["
                + t.getName() + "]";
    }

    @XmlEnum
    public enum Level {
        DEBUG,
        INFO,
        WARN,
        ERROR,
        NONE;
    }
}
